#include <fstream>
#include "PolyMesh.hpp"
#include <sstream>
#include "TetgenFile.hpp"
#include <assert.h>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>

using namespace std;
namespace
{
   typedef boost::tuple<int,int,int> TetFace;
   typedef std::pair<int,int> TetPair;
   typedef std::map<TetFace, TetPair> TetFaceMap;
   void GetTetFace(int x, int y, int z, TetFace& aFace, bool& aIsReverse) 
   { 
      aFace = TetFace(x,y,z); 
      if (aFace.get<0>() > aFace.get<2>()) std::swap(aFace.get<0>(), aFace.get<2>());
      if (aFace.get<1>() > aFace.get<2>()) std::swap(aFace.get<1>(), aFace.get<2>());
      if (aFace.get<0>() > aFace.get<1>()) std::swap(aFace.get<0>(), aFace.get<1>());
      int eq = ((x == aFace.get<0>()) ? 1 : 0)
               + ((y == aFace.get<1>()) ? 1 : 0)
               + ((z == aFace.get<2>()) ? 1 : 0);
      bool sameOrder = (eq == 0 || eq == 3);
      aIsReverse = !sameOrder;
   }
   void AddFace(TetFaceMap& aFaceMap,
                int         aTetId,
                int         x,
                int         y,
                int         z)
   {
      bool reversed=false;
      TetFace face;
      GetTetFace(x,y,z, face, reversed);
      TetFaceMap::iterator i = aFaceMap.find(face);
      if (i == aFaceMap.end())
      {
         TetPair& p = aFaceMap[face];
         p.first = p.second = -1;
      }
      if (! reversed) 
         aFaceMap[face].first = aTetId;
      else
         aFaceMap[face].second = aTetId;
   }
}
bool TetgenFile::Read(const std::string& aNodeFile,
                      PolyMesh&     aMesh)
{
   bool ok = true;
   std::string file = aNodeFile;
   size_t dot = file.find_last_of('.');
   if (dot == std::string::npos)
   {
      return false;
   }

   string eleFile = file.substr(0, dot) + ".ele";
   string faceFile = file.substr(0, dot) + ".face";

   aMesh.Clear();

   // read vertices
   {
      int vertCount, garb;
      ifstream io(aNodeFile.c_str());
      if (io >> vertCount >> garb >> garb >> garb)
      {
         aMesh.verts.resize(vertCount);
         for (int i = 0; i < vertCount; ++i)
         {
            Vec3& v = aMesh.verts[i];
            io >> garb >> v.x >> v.y >> v.z;
         }
      }
      else
      {
         ok = false;
      }
   }

   // read elements
   {
      int tetCount, garb;
      ifstream io(eleFile.c_str());
      if (io >> tetCount >> garb >> garb)
      {
         Polyhedron t;
         t.arity = 4;
         char line[1000];
         io.getline(line, 1000);
         for (int i = 0; i < tetCount; ++i)
         {
            //Tetra& t = aMesh.tetra[i];
            t.material = -1;
            io.getline(line, 1000);
            std::stringstream ss(line);
            //io >> garb >> t.vertex[0] >> t.vertex[1] >> t.vertex[2] >> t.vertex[3];
            ss >> garb >> t.vertex[0] >> t.vertex[1] >> t.vertex[2] >> t.vertex[3] >> t.material;
            assert(garb == i+1);
            --t.vertex[0];
            --t.vertex[1];
            --t.vertex[2];
            --t.vertex[3];
            aMesh.polys.push_back(t);
         }
      }
      else
      {
         ok = false;
      }
   }

   aMesh.Recompute();

   return ok;
   TetFaceMap faceMap;
   PolyhedronList& polys = *aMesh.polys;
   for (size_t i = 0; i < polys.size(); ++i)
   {
      const Polyhedron& p = polys[i];
      int vertOrder[] = { 0,1,2, 3,1,0, 3,2,1, 3,0,2 };
      int ind = 0;
      for (int f = 0; f < 4; ++f)
      {
         int x = p.vertex[vertOrder[ind++]];
         int y = p.vertex[vertOrder[ind++]];
         int z = p.vertex[vertOrder[ind++]];
         AddFace(faceMap, i, x, y, z);
      }
   }

   // Read faces (and materials)
   if (true)
   {
      ifstream io(faceFile.c_str());
      int faceCount, garb;
      if (io >> faceCount >> garb)
      {
         for (int i = 0; i < faceCount; ++i)
         {
            int faceNumber, verts[3], material;
            io >> faceNumber >> verts[0] >> verts[1] >> verts[2] >> material;
            --verts[0];
            --verts[1];
            --verts[2];
            bool reverse;
            TetFace tetFace;
            GetTetFace(verts[0], verts[1], verts[2], tetFace, reverse);
            TetFaceMap::iterator iter = faceMap.find(tetFace);
            if (iter != faceMap.end())
            {
               TetPair p = iter->second;
               int tetid = reverse ? p.second : p.first;
               if (tetid != -1)
               {
                  Polyhedron& poly = polys[tetid];
                  poly.material = material;
               }
            }
         }
      }

      //// Propagate colors to neighboring tets
      //bool workDone = true;
      //while (workDone)
      //{
      //   workDone = false;
      //   for (TetFaceMap::iterator i = faceMap.begin(); i != faceMap.end();)
      //   {
      //      bool done = false;
      //      TetPair& tpair = i->second;
      //      if (tpair.first != tpair.second && tpair.first != -1 && tpair.second != -1)
      //      {
      //         Polyhedron& p1 = polys[tpair.first];
      //         Polyhedron& p2 = polys[tpair.second];
      //         if (p1.material == -1 && p2.material != -1)
      //         {
      //            p1.material = p2.material;
      //            workDone = done = true;
      //         }
      //         else if (p1.material != -1 && p2.material == -1)
      //         {
      //            p2.material = p1.material;
      //            workDone = done = true;
      //         }
      //      }
      //      else
      //      {
      //         done = true;
      //      }

      //      if (done)
      //      {
      //         i = faceMap.erase(i);
      //      }
      //      else
      //      {
      //         ++i;
      //      }
      //   }
      //}
      //for (size_t i = 0; i < polys.size(); ++i)
      //{
      //   const Polyhedron& p = polys[i];
      //   assert(p.material != -1);
      //}
      for (size_t i = 0; i < polys.size(); ++i)
      {
         Polyhedron& p = polys[i];
         if (p.material == -1)
         {
            p.material = 1;
         }
      }
   }
   return ok;
}
